#ifndef GRAPHICS_INL
#define GRAPHICS_INL
#include "zhngdi.hpp"
NAMESPACE_ZHNGDI_L

constexpr int APPEND_WIDTH = 16;
constexpr int APPEND_HEIGHT = 39;

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
std::vector<PFigure> Figure::_figures;

std::wstring String_toWString(const std::string s)
{
    int nLen = s.size();
    wchar_t* buffer = new wchar_t[nLen + 1];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), nLen, buffer, nLen);
    buffer[nLen] = '\0';
    std::wstring wstr = buffer;
    delete[] buffer;
    return wstr;
}

void Figure::Initialize(int width, int height, const std::string CaptionName)
{
    _id = _figures.size() + 1;
    errCode = NORMAL;
    char figcnt = _id + 0x30;
    std::string classname(1, figcnt);
    _size = POINT2D(width + APPEND_WIDTH, height + APPEND_HEIGHT);
    _PenColor = Gdiplus::Color(255, 0, 0, 0);
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    Gdiplus::GdiplusStartup(&_gdiplusToken, &gdiplusStartupInput, NULL);
    WNDCLASS wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = GetModuleHandle(NULL);
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
#ifdef _MSVC_LANG
    std::wstring wclassname = String_toWString(classname);
    std::wstring wCaptionName = String_toWString(CaptionName);
    wndclass.lpszClassName = wclassname.c_str();
    _hWnd = CreateWindow(wclassname.c_str(), wCaptionName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
        CW_USEDEFAULT, _size.x, _size.y, NULL, NULL, GetModuleHandle(NULL), NULL);
#else
    wndclass.lpszClassName = classname.c_str();
    _hWnd = CreateWindow(classname.c_str(), CaptionName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
        CW_USEDEFAULT, _size.x, _size.y, NULL, NULL, GetModuleHandle(NULL), NULL);
#endif
    ZHNGDI_ASSERT_ERROR(_hWnd, "Create window failed!");
    ShowWindow(_hWnd, SW_SHOW);
    _font = new Gdiplus::Font(L"Times New Roman", 14, Gdiplus::FontStyleRegular, Gdiplus::UnitPixel);
    _pen = new Gdiplus::Pen(_PenColor);
    _brush = new Gdiplus::SolidBrush(_PenColor);
    _bitmap = new Gdiplus::Bitmap(width, height);
    _graphics = new Gdiplus::Graphics(_bitmap);
    _closed = false;
    _figures.push_back(this);
}

Figure::Figure(int width, int height, const std::string CaptionName) {
    Initialize(width, height, CaptionName);
}
Figure::Figure(const std::string ImageName, const std::string CaptionName) {
    Initialize(640, 480, CaptionName); Add_Image(ImageName, 0, 0);
}
void Figure::Set_PenColor(Gdiplus::ARGB argb) {
    _PenColor.SetValue(argb); _pen->SetColor(_PenColor); _brush->SetColor(_PenColor);
}
POINT2D Figure::Get_WindowSize() { return POINT2D(_bitmap->GetWidth(), _bitmap->GetHeight()); };
Gdiplus::Bitmap* Figure::Get_pBitmap() { return _bitmap; }
void Figure::Close_All() { PostQuitMessage(0); }
void Figure::Draw_Pixel(int x, int y) {
    _bitmap->SetPixel(x, y, _PenColor);
}
void Figure::Draw_Line(int x1, int y1, int x2, int y2) {
    _graphics->DrawLine(_pen, x1, y1, x2, y2);
}
void Figure::Draw_Rectangle(int x1, int y1, int x2, int y2) {
    _graphics->DrawRectangle(_pen, x1, y1, x2 - x1, y2 - y1);
}
void Figure::Draw_Circle(int x, int y, int r) {
    _graphics->DrawEllipse(_pen, x - r, y - r, r + r, r + r);
}
void Figure::Draw_Ellipse(int x, int y, int width, int height) {
    _graphics->DrawEllipse(_pen, x, y, width + width, height + height);
}

void Figure::Add_Image(const std::string ImageName, int x, int y, bool resize)
{
    std::wstring ws = String_toWString(ImageName);
    Gdiplus::Bitmap bitmap(ws.c_str());
    Gdiplus::Color color;
    int w = bitmap.GetWidth();
    int h = bitmap.GetHeight();
    if (resize) {
        RECT wh;
        GetWindowRect(_hWnd, &wh);
        MoveWindow(_hWnd, wh.top, wh.left, w + APPEND_WIDTH, h + APPEND_HEIGHT, true);
        delete _bitmap;
        _bitmap = new Gdiplus::Bitmap(w, h);
    }
    for (int i = 0; i < w; ++i) {
        for (int j = 0; j < h; ++j) {
            bitmap.GetPixel(i, j, &color);
            _bitmap->SetPixel(x + i, y + j, color);
        }
    }
}

ColorRGB Figure::Get_PixelColor(int x, int y)
{
    Gdiplus::Color color;
    if (x < (int)_bitmap->GetWidth() && y < (int)_bitmap->GetHeight()) {
        _bitmap->GetPixel(x, y, &color);
        return color.GetValue() & 0xFFFFFF;
    }
    if (!(errCode & INDEX_OUT)) {
        ZHNGDI_ASSERT_WARNING(false, "Pixel index out of bounds.");
        errCode |= INDEX_OUT;
    }
    return 0;
}

void Figure::Draw_Text(int x, int y, std::string s)
{
    std::wstring ws = String_toWString(s);
    _graphics->DrawString(ws.c_str(), -1, _font, Gdiplus::PointF(x, y), _brush);
}

void Figure::Window_Loop()
{
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

void Figure::Close_Figure(WINDOWS_CLOSE state)
{
    for (DWORD i = 0; i < _figures.size(); ++i) {
        if (this->_id == _figures[i]->_id) {
            Safe_Delete(_font);
            Safe_Delete(_pen);
            Safe_Delete(_brush);
            Safe_Delete(_bitmap);
            Safe_Delete(_graphics);
            _figures.erase(_figures.begin() + i);
        }
    }
    if (_closed) return;
    if (_figures.size() == 0)
        PostQuitMessage(0);
    switch (state) {
    case NOOP: break;
    case MINIMIZE: CloseWindow(_hWnd); break;
    case CLOSE: DestroyWindow(_hWnd); break;
    default: break;
    }
    Gdiplus::GdiplusShutdown(_gdiplusToken);
    _closed = true;
}

PFigure Figure::WindowHandle_toPFigure(HWND hWnd)
{
    char classname[2];
    ZHNGDI_ASSERT_ERROR(GetClassNameA(hWnd, classname, 2), "GetClassName failed!");
    int id = classname[0] - 0x30;
    if (id <= 0) return nullptr;
    for (DWORD i = 0; i < _figures.size(); ++i)
        if (id == _figures[i]->_id)
            return _figures[i];
    return nullptr;
}

inline void onPaint(HWND hWnd, HDC hdc)
{
    PFigure figure = Figure::WindowHandle_toPFigure(hWnd);
    ZHNGDI_ASSERT_ERROR(figure, "Get figure pointer failed!");
    Gdiplus::Graphics graphics(hdc);
    graphics.DrawImage(figure->Get_pBitmap(), 0, 0);
}

inline void onKeyDown(HWND hWnd, WPARAM wParam)
{
    PFigure figure = Figure::WindowHandle_toPFigure(hWnd);
    switch (wParam) {
    case VK_SPACE: figure->Close_Figure(); break;
    default: break;
    }
}

inline void onClose(HWND hWnd)
{
    PFigure figure = Figure::WindowHandle_toPFigure(hWnd);
    figure->Close_Figure();
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    switch (message) {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        onPaint(hWnd, hdc);
        EndPaint(hWnd, &ps);
        return 0;
    case WM_KEYDOWN: onKeyDown(hWnd, wParam); return 0;
    case WM_CLOSE: onClose(hWnd); return 0;
    default: return DefWindowProc(hWnd, message, wParam, lParam);
    }
}

Figure::~Figure()
{
    Close_Figure();
}

NAMESPACE_ZHNGDI_R
#endif
